home *** CD-ROM | disk | FTP | other *** search
- /* +-------------------------------------------------------------------+ */
- /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com) | */
- /* | | */
- /* | Permission to use, copy, modify, and to distribute this software | */
- /* | and its documentation for any purpose is hereby granted without | */
- /* | fee, provided that the above copyright notice appear in all | */
- /* | copies and that both that copyright notice and this permission | */
- /* | notice appear in supporting documentation. There is no | */
- /* | representations about the suitability of this software for | */
- /* | any purpose. this software is provided "as is" without express | */
- /* | or implied warranty. | */
- /* | | */
- /* +-------------------------------------------------------------------+ */
-
- #include <X11/Intrinsic.h>
- #include <X11/StringDefs.h>
- #include <X11/cursorfont.h>
- #ifndef NOSTDHDRS
- #include <stdlib.h>
- #include <unistd.h>
- #endif
- #include "Paint.h"
- #include "misc.h"
- #include "xpaint.h"
-
- typedef struct {
- Boolean done;
- } LocalInfo;
-
- #define ZINDEX8(x, y, img) ((y) * img->bytes_per_line) + (x)
-
- static Drawable drawable;
- static Display *dpy;
- static XImage *source;
- static Boolean *mask;
- static int maskW, maskH;
- static int xMin, yMin, xMax, yMax;
- static int timeCount = 0;
- static int fillMode = 0;
-
-
- /*
- * A Seed Fill Algorithm
- * by Paul Heckbert
- * from "Graphics Gems", Academic Press, 1990
- */
-
- /*
- * fill.c : simple seed fill program
- * Calls pixelread() to read pixels, pixelwrite() to write pixels.
- *
- * Paul Heckbert 13 Sept 1982, 28 Jan 1987
- */
-
- #define pixelwrite(x,y) do { \
- mask[y * maskW + x] = True; \
- xMin = MIN(xMin, x); \
- yMin = MIN(yMin, y); \
- xMax = MAX(xMax, x); \
- yMax = MAX(yMax, y); \
- } while (0)
-
- #define STEP 32
-
- static Pixel pixelread(int x, int y, Boolean first)
- {
- static Pixel ov;
- static int xMin, yMin, xMax, yMax;
- Pixel p;
- int n;
-
- if (first) {
- xMin = MAX(x - STEP, 0);
- yMin = MAX(y - STEP, 0);
- xMax = MIN(x + STEP, maskW);
- yMax = MIN(y + STEP, maskH);
-
- XGetSubImage(dpy, drawable, xMin, yMin,
- xMax - xMin, yMax - yMin,
- AllPlanes, ZPixmap, source,
- xMin, yMin);
-
- ov = XGetPixel(source, x, y);
-
- return ov;
- } else if (mask[y * maskW + x]) {
- return ~ov;
- }
-
- if (x < xMin) {
- n = MAX(x - STEP, 0);
- XGetSubImage(dpy, drawable, n, yMin,
- xMin - n, yMax - yMin,
- AllPlanes, ZPixmap, source,
- n, yMin);
- xMin = n;
- }
- if (y < yMin) {
- n = MAX(y - STEP, 0);
- XGetSubImage(dpy, drawable, xMin, n,
- xMax - xMin, yMin - n,
- AllPlanes, ZPixmap, source,
- xMin, n);
- yMin = n;
- }
- if (x >= xMax) {
- n = MIN(x + STEP, maskW);
- XGetSubImage(dpy, drawable, xMax, yMin,
- n - xMax, yMax - yMin,
- AllPlanes, ZPixmap, source,
- xMax, yMin);
- xMax = n;
- }
- if (y >= yMax) {
- n = MIN(y + STEP, maskH);
- XGetSubImage(dpy, drawable, xMin, yMax,
- xMax - xMin, n - yMax,
- AllPlanes, ZPixmap, source,
- xMin, yMax);
- yMax = n;
- }
-
- /*
- ** Do it fast for those 8 bit displays...
- */
- if (source->bits_per_pixel == 8) {
- p = source->data[ZINDEX8(x, y, source)];
- } else {
- p = XGetPixel(source, x, y);
- }
- return p;
- }
-
- /*
- ** Exange pixel 1 for filled pixel value 2
- */
- static void change(int sx, int sy, int width, int height)
- {
- int x, y;
- Pixel pix = XGetPixel(source, sx, sy);
-
- if (source->bits_per_pixel == 8) {
- for (y = 0; y < height; y++) {
- for (x = 0; x < width; x++)
- if (source->data[ZINDEX8(x, y, source)] == pix)
- pixelwrite(x, y);
- if (y % 100 == 0)
- StateTimeStep();
- }
- } else {
- for (y = 0; y < height; y++) {
- for (x = 0; x < width; x++)
- if (XGetPixel(source, x, y) == pix)
- pixelwrite(x, y);
- if (y % 64 == 0)
- StateTimeStep();
- }
- }
- }
-
- typedef struct {short y, xl, xr, dy;} Segment;
- /*
- * Filled horizontal segment of scanline y for xl<=x<=xr.
- * Parent segment was on line y-dy. dy=1 or -1
- */
-
- #define STACKSIZE 20000 /* max depth of stack */
-
- #define PUSH(Y, XL, XR, DY) /* push new segment on stack */ \
- if (sp<stack+STACKSIZE && Y+(DY)>=0 && Y+(DY)<=height) \
- {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
-
- #define POP(Y, XL, XR, DY) /* pop segment off stack */ \
- {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
-
- /*
- * fill: set the pixel at (x,y) and all of its 4-connected neighbors
- * with the same pixel value to the new pixel value nv.
- * A 4-connected neighbor is a pixel above, below, left, or right of a pixel.
- */
-
- static void fill(int x, int y, int width, int height)
- {
- int start, x1, x2, dy = 0;
- Pixel ov; /* old pixel value */
- Segment stack[STACKSIZE], *sp = stack; /* stack of filled segments */
-
- ov = pixelread(x, y, True); /* read pv at seed point */
- PUSH(y, x, x, 1); /* needed in some cases */
- PUSH(y+1, x, x, -1); /* seed segment (popped 1st) */
-
- while (sp>stack) {
- /* pop segment off stack and fill a neighboring scan line */
- POP(y, x1, x2, dy);
- /*
- * segment of scan line y-dy for x1<=x<=x2 was previously filled,
- */
- for (x = x1; x >= 0 && pixelread(x, y, False) == ov; x--)
- pixelwrite(x, y);
- if (x >= x1) {
- for (x++; x<=x2 && pixelread(x, y, False)!=ov; x++);
- start = x;
- if (x > x2)
- continue;
- } else {
- start = x+1;
- if (start<x1) PUSH(y, start, x1-1, -dy); /* leak on left? */
- x = x1+1;
- }
- do {
- for (; x<=width && pixelread(x, y, False)==ov; x++)
- pixelwrite(x, y);
- PUSH(y, start, x-1, dy);
- if (x>x2+1) PUSH(y, x2+1, x-1, -dy); /* leak on right? */
- for (x++; x<=x2 && pixelread(x, y, False)!=ov; x++);
- start = x;
- } while (x<=x2);
-
- if (++timeCount % 32 == 0)
- StateTimeStep();
- }
- }
-
- static void press(Widget w, LocalInfo *l, XButtonEvent *event, OpInfo *info)
- {
- XRectangle undo, rect;
- int width, height;
- GC gc, dgc;
- XGCValues values;
- int isBusy, depth;
- Pixmap pix;
-
- if (event->button == Button1)
- dgc = info->first_gc;
- else if (event->button == Button2)
- dgc = info->second_gc;
- else
- return;
-
- dpy = XtDisplay(w);
- drawable = info->drawable;
-
- UndoStart(w, info);
-
- XtVaGetValues(w, XtNdrawWidth, &width,
- XtNdrawHeight, &height,
- XtNdepth, &depth,
- NULL);
- if (isBusy = (width * height > 6400))
- StateSetBusy(True);
-
- if ((mask = (Boolean*)XtCalloc(sizeof(Boolean), width * height)) == NULL)
- return;
- maskW = width;
- maskH = height;
-
- memset(mask, False, width * height * sizeof(Boolean));
-
- if (fillMode == 0) {
- source = NewXImage(dpy, NULL, depth, width, height);
- } else {
- source = PwGetImage(w, NULL);
- }
-
- xMin = xMax = info->realX;
- yMin = yMax = info->realY;
-
- if (fillMode == 0)
- fill(info->realX, info->realY, width - 1, height - 1);
- else
- change(info->realX, info->realY, width, height);
-
- rect.x = xMin;
- rect.y = yMin;
- rect.width = (xMax - xMin) + 1;
- rect.height = (yMax - yMin) + 1;
-
- pix = MaskDataToPixmap(w, width, height, mask, &rect);
-
- XSetClipOrigin(dpy, dgc, rect.x, rect.y);
- XSetClipMask(dpy, dgc, pix);
-
- XFillRectangles(dpy, info->drawable, dgc, &rect, 1);
-
- if (!info->isFat)
- XFillRectangles(dpy, XtWindow(w), dgc, &rect, 1);
-
- XSetClipMask(dpy, dgc, None);
- XFreePixmap(dpy, pix);
-
- XYtoRECT(xMin, yMin, xMax + 1, yMax + 1, &undo);
- UndoSetRectangle(w, &undo);
- PwUpdate(w, &undo, False);
-
- if (fillMode == 0)
- XDestroyImage(source);
-
- if (isBusy)
- StateSetBusy(False);
- }
-
- void *FillAdd(Widget w)
- {
- LocalInfo *l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
- l->done = False;
- OpAddEventHandler(w, opPixmap, ButtonPressMask, FALSE, (OpEventProc)press, l);
- SetCrossHairCursor(w);
- return l;
- }
- void FillRemove(Widget w, LocalInfo *l)
- {
- OpRemoveEventHandler(w, opPixmap, ButtonPressMask, FALSE, (OpEventProc)press, l);
- XtFree((XtPointer)l);
- }
-
- void FillSetMode(int value)
- {
- fillMode = value;
- }
-
- int FillGetMode()
- {
- return fillMode;
- }
-